package com.weaver.qfengx;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.weaver.qfengx.entity.FormFileItem;
import com.weaver.qfengx.map.CaseInsensitiveMap;
import com.weaver.qfengx.okhttp.body.RequestJsonBody;
import com.weaver.qfengx.okhttp.ssl.SSLSocketClient;
import okhttp3.*;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.junit.Test;

import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 请求工具类，底层依赖于OkHttp
 * Created by YeShengtao on 2020/9/25 17:41
 */
public class RequestUtils {


    /**
     * 获取IP
     *
     * @param request HttpServletRequest
     * @return
     */
    public static String ip(HttpServletRequest request) {
        String ip = null;
        //X-Forwarded-For：Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP：apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP：weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP：有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP：nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }
        //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }
        //还是不能获取到，最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 上传文件以及参数，POST请求方式
     */
    public static Response uploadFileAndParam(OkHttpClient client, String url, File file,
                                              Map<String, String> params) throws IOException {
        return uploadFileAndParam(client, url, file, Maps.newHashMap(), params);
    }

    /**
     * 上传文件以及参数，POST请求方式
     */
    public static Response uploadFileAndParam(OkHttpClient client, String url, File file,
                                              Map<String, String> headers, Map<String, String> params) throws IOException {
        return uploadFileAndParam(client, url, new FileInputStream(file), "file", file.getName(), headers, params);
    }

    public static Response uploadFileAndParam(OkHttpClient client, String url, InputStream inputStream, String fileKeyName, String filename, Map<String, String> headers, Map<String, String> params) throws IOException {
        return uploadFileAndParam(client, url, inputStream == null ? null : IOUtils.toByteArray(inputStream), fileKeyName, filename, headers, params);
    }

    public static Response uploadFileAndParam(OkHttpClient client, String url, InputStream inputStream, String fileKeyName, String filename, Map<String, String> params) throws IOException {
        return uploadFileAndParam(client, url, inputStream, fileKeyName, filename, Maps.newHashMap(), params);
    }

    public static Response uploadFileAndParam(String url, InputStream inputStream, String fileKeyName, String filename, Map<String, String> params) throws IOException {
        return uploadFileAndParam(defaultClient(), url, inputStream, fileKeyName, filename, params);
    }

    public static Response uploadFileAndParam(OkHttpClient client, String url, byte[] bytes, String fileKeyName, String filename, Map<String, String> headers, Map<String, String> params) throws IOException {
        MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder();
        // 凭借请求头信息
        Headers.Builder hbuilder = headerBuilder(headers);

        // 拼接请求参数信息
        for (Map.Entry<String, String> entry : params.entrySet()) {
            multipartBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
        }
        RequestBody requestBody = null;
        if (bytes != null) {
            RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), bytes);
            requestBody = multipartBodyBuilder
                    .setType(MultipartBody.FORM)
                    .addFormDataPart(fileKeyName, filename, fileBody)
                    .build();


        } else {
            requestBody = multipartBodyBuilder
                    .setType(MultipartBody.FORM)
                    .build();
        }

        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .headers(hbuilder.build())
                .build();
        return client.newCall(request).execute();
    }

    private static Headers.Builder headerBuilder(Map<String, String> headers) {
        Headers.Builder hbuilder = new Headers.Builder();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            hbuilder.add(entry.getKey(), entry.getValue());
        }
        return hbuilder;
    }

    private static FormBody.Builder formBodyBuilder(Map<String, String> params) {
        FormBody.Builder formBody = new FormBody.Builder();
        // 拼接请求参数信息
        for (Map.Entry<String, String> entry : params.entrySet()) {
            formBody.add(entry.getKey(), entry.getValue());
        }
        return formBody;
    }

    private static OkHttpClient defaultClient() {
        return new OkHttpClient();
    }

    public static Response doPost(String url, Map<String, String> params) throws IOException {
        return doPost(url, Maps.newHashMap(), params);
    }

    public static OkHttpClient defaultIgnoreSSLClient() {
        return new OkHttpClient.Builder()
                .sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), SSLSocketClient.x509TrustManager())
                .hostnameVerifier(SSLSocketClient.getHostnameVerifier()).build();
    }

    public static OkHttpClient defaultIgnoreSSLClient(long timeout, TimeUnit timeUnit) {
        return new OkHttpClient.Builder()
                .sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), SSLSocketClient.x509TrustManager())
                .hostnameVerifier(SSLSocketClient.getHostnameVerifier())
                .connectTimeout(timeout, timeUnit)
                .build();
    }

    /**
     * POST请求 json格式
     *
     * @param url
     * @param headers
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doPostByJson(OkHttpClient client, String url, Map<String, String> headers, Map<String, Object> params) throws IOException {
        Headers.Builder hbuilder = headerBuilder(headers);
        MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");
        RequestBody requestBody = RequestJsonBody.createBody(mediaType, JSONObject.toJSONString(params));
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .headers(hbuilder.build())
                .build();
        return client.newCall(request).execute();
    }

    public static Response doPostByJson(OkHttpClient client, String url, String jsonStr) throws IOException {
        return doPostByJson(client, url, Maps.newHashMap(), jsonStr);
    }

    public static Response doPostByJson(String url, Map<String, String> headers, Map<String, Object> params) throws IOException {
        return doPostByJson(defaultClient(), url, headers, params);
    }

    public static Response doPostByJson(String url, Map<String, Object> params) throws IOException {
        return doPostByJson(url, Maps.newHashMap(), params);
    }

    public static Response doPostByJson(OkHttpClient client, String url, Map<String, Object> params) throws IOException {
        return doPostByJson(client, url, Maps.newHashMap(), params);
    }

    public static Response doPostByJson(OkHttpClient client, String url, Map<String, String> headers, String paramJson) throws IOException {
        Headers.Builder hbuilder = headerBuilder(headers);
        MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");
        RequestBody requestBody = RequestJsonBody.createBody(mediaType, paramJson);
//        RequestBody requestBody = RequestBody.create(mediaType, paramJson);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .headers(hbuilder.build())
                .build();
        return client.newCall(request).execute();
    }


    /**
     * POST请求
     *
     * @param url
     * @param headers
     * @param params
     * @return
     */

    public static Response doPost(String url, Map<String, String> headers, Map<String, String> params) throws IOException {
        return doPost(defaultClient(), url, headers, params);
    }

    public static Response doPost(OkHttpClient client, String url, Map<String, String> params) throws IOException {
        return doPost(defaultClient(), url, Maps.newHashMap(), params);
    }

    public static Response doPost(OkHttpClient client, String url, Map<String, String> headers, Map<String, String> params) throws IOException {
        Headers.Builder hbuilder = headerBuilder(headers);
        FormBody.Builder bodyBuilder = formBodyBuilder(params);
        Request request = new Request.Builder()
                .url(url)
                .post(bodyBuilder.build())
                .headers(hbuilder.build())
                .build();
        return client.newCall(request).execute();
    }

    public static Response doGet(String url) throws IOException {
        return doGet(url, Maps.newHashMap());
    }

    public static Response doGet(String url, Map<String, String> params) throws IOException {
        return doGet(url, Maps.newHashMap(), params);
    }

    public static Response doGet(String url, Map<String, String> headers, Map<String, String> params) throws IOException {
        return doGet(defaultClient(), url, headers, params);
    }

    public static Response doGet(OkHttpClient client, String url, Map<String, String> headers, Map<String, String> params) throws IOException {
        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(url))
                .newBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            urlBuilder.addQueryParameter(entry.getKey(), StringUtils.chinaToUnicode(entry.getValue()));
        }
        Request request = new Request.Builder()
                .headers(headerBuilder(headers).build())
                .url(urlBuilder.build()).build();
        return client.newCall(request).execute();
    }

    public static Response doGetWithoutChange(OkHttpClient client, String url, Map<String, String> headers, Map<String, String> params) throws IOException {
        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(url))
                .newBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
        }
        Request request = new Request.Builder()
                .headers(headerBuilder(headers).build())
                .url(urlBuilder.build()).build();
        return client.newCall(request).execute();
    }

    public static Response doGetWithoutChange(OkHttpClient client, String url, Map<String, String> params) throws IOException {
        return doGetWithoutChange(client, url, Maps.newHashMap(), params);
    }

    public static Response doGetWithoutChange(String url, Map<String, String> params) throws IOException {
        return doGetWithoutChange(defaultClient(), url, Maps.newHashMap(), params);
    }

    public static Response doGetWithoutChange(String url, Map<String, String> headers, Map<String, String> params) throws IOException {
        return doGetWithoutChange(defaultClient(), url, headers, params);
    }

    /**
     * 创建代理客户端
     * @param host 代理主机
     * @param port 代理端口
     * @param username 代理用户名
     * @param password 密码
     * @return
     */
    public static OkHttpClient createProxyClient(Proxy.Type proxyType, String host, int port, String username, String password) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.proxy(new Proxy(proxyType, new InetSocketAddress(host, port)));
        if (StringUtils.isNotEmpty(username)) { // 说明有用户
            builder.authenticator((route, response) -> {
                String credential = Credentials.basic(username, password);
                return response.request().newBuilder()
                        .header("Proxy-Authorization", credential)
                        .build();
            });
        }
        return builder.build();
    }

    @Test
    public void test() throws IOException {
        OkHttpClient client = new OkHttpClient();
        Map<String, String> params = Maps.newHashMap();
        params.put("requestid", "1");
        params.put("token", "1");
        params.put("fieldname", "1");
        params.put("user", "1");
        File file = new File("C:\\Users\\Qfeng\\Desktop\\down\\test.jpg");
        Response response = RequestUtils.uploadFileAndParam(client,
                "http://localhost:89/api/seconddev/hxbank/prtc/upload", file, params);
        System.out.println(response.body().string());
        System.out.println(response.isSuccessful());
    }

    /**
     * request中提取参数封装到Map中
     * post请求根据content-type自动转换
     * 返回大小写不敏感的Map
     * @param request
     * @return
     */
    public static Map<String, Object> getParamsMap(HttpServletRequest request) {
        Map<String, Object> params = new CaseInsensitiveMap<>();
        Enumeration<String> em = request.getParameterNames();
        while(em.hasMoreElements()){
            String paramname = em.nextElement();
            params.put(paramname, request.getParameter(paramname));
        }
        // 判断Content-type
        String contentType = StringUtils.val(request.getContentType(), "");
        if (contentType.startsWith("multipart/form-data")) { // 说明是表单
            try {
                DiskFileItemFactory factory = new DiskFileItemFactory();
                ServletFileUpload upload = new ServletFileUpload(factory);
                List<FileItem> files = upload.parseRequest(request);
                for (FileItem fileItem : files) {
                    String key = fileItem.getFieldName();
                    if (fileItem.isFormField()) {
                        params.put(key, fileItem.getString("utf-8"));
                    } else { // 附件
                        params.put(key, new FormFileItem(fileItem));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return params;
    }


    @Test
    public void test01() {
//        String name = "user-requestid-fieldn--ame";
//        System.out.println(Splitter.on("-").trimResults().omitEmptyStrings().split(name).toString());
        File file = new File("C:\\Users\\Qfeng\\Desktop\\upload\\prtc\\user-requestid-fieldn--ame\\11.txt");
        file.delete();
    }
}
